package icu.zhhll.util.time;

import org.apache.commons.lang3.time.FastDateFormat;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 日期工具类
 *
 * @author zh
 * @date 2023/7/21 16:24
 */
public class DatesUtil {
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final FastDateFormat FORMAT_NORMAL_DATETIME = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
    /**
     * yyyy-MM-dd
     */
    public static final FastDateFormat FORMAT_NORMAL_DATE = FastDateFormat.getInstance("yyyy-MM-dd");
    /**
     * FORMAT_HHmmss
     */
    public static final FastDateFormat FORMAT_NORMAL_TIME = FastDateFormat.getInstance("HH:mm:ss");
    /**
     * yyyyMMdd
     */
    public static final FastDateFormat FORMATDATE = FastDateFormat.getInstance("yyyyMMdd");
    /**
     * yyyy_MM_dd_HH_mm_ss
     */
    public static final FastDateFormat FORMAT_FILR_DATETIME = FastDateFormat.getInstance("yyyy_MM_dd_HH_mm_ss");

    /**
     * MM/dd/yyyy
     */
    public static final FastDateFormat FORMAT_MDY_DATE = FastDateFormat.getInstance("MM/dd/yyyy");

    /**
     * yyyy/MM/dd
     */
    public static final FastDateFormat FORMAT_YMD_DIAGONAL_DATE = FastDateFormat.getInstance("yyyy/MM/dd");

    /**
     * yyyy年MM月dd日
     */
    public static final FastDateFormat FORMAT_CH_DATE = FastDateFormat.getInstance("yyyy年MM月dd日");

    /**
     * yyyy年MM月
     */
    public static final FastDateFormat FORMAT_CHYM_DATE = FastDateFormat.getInstance("yyyy年MM月");

    /**
     * yyyy年MM月dd日 HH:MM
     */
    public static final FastDateFormat FORMAT_CHYMDHM_DATE = FastDateFormat.getInstance("yyyy年MM月dd日 HH:mm");

    /**
     * MM.dd
     */
    public static final FastDateFormat FORMAT_MD_DATE = FastDateFormat.getInstance("MM.dd");

    /**
     * HH:mm
     */
    public static final FastDateFormat FORMAT_HM_DATE = FastDateFormat.getInstance("HH:mm");

    /**
     * HH
     */
    public static final FastDateFormat FORMAT_HH_DATE = FastDateFormat.getInstance("HH");

    /**
     * yyyy.MM.dd
     */
    public static final FastDateFormat FORMAT_YMD_DATE = FastDateFormat.getInstance("yyyy.MM.dd");

    /**
     * yyyyMM
     */
    public static final FastDateFormat FORMAT_YM_DATE = FastDateFormat.getInstance("yyyyMM");


    /**
     * 获取当天的开始时间
     *
     * @return
     */
    public static Date getDayBegin() {
        LocalDateTime todayBegin = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        return Date.from(todayBegin.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取当天的结束时间
     */
    public static Date getDayEnd() {
        LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
        return Date.from(todayEnd.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取昨天的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfYesterday() {
        LocalDateTime beginDayOfYesterday = LocalDateTime.of(LocalDate.now().plusDays(-1), LocalTime.MIN);

        return Date.from(beginDayOfYesterday.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取昨天的结束时间
     *
     * @return
     */
    public static Date getEndDayOfYesterDay() {
        LocalDateTime endDayOfYesterday = LocalDateTime.of(LocalDate.now().plusDays(-1), LocalTime.MAX);

        return Date.from(endDayOfYesterday.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取明天的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfTomorrow() {
        LocalDateTime beginDayOfTomorrow = LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN);

        return Date.from(beginDayOfTomorrow.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取明天的结束时间
     *
     * @return
     */
    public static Date getEndDayOfTomorrow() {
        LocalDateTime endDayOfTomorrow = LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MAX);

        return Date.from(endDayOfTomorrow.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取本周的开始时间
     */
    public static Date getBeginDayOfWeek() {
        LocalDateTime beginDayOfWeek = LocalDateTime.of(LocalDate.now().with(DayOfWeek.MONDAY), LocalTime.MIN);
        return Date.from(beginDayOfWeek.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取本周的结束时间
     *
     * @return
     */
    public static Date getEndDayOfWeek() {
        LocalDateTime endDayOfWeek = LocalDateTime.of(LocalDate.now().with(DayOfWeek.MONDAY), LocalTime.MAX);
        return Date.from(endDayOfWeek.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取上周的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfLastWeek() {
        LocalDateTime beginDayOfLastWeek = LocalDateTime.of(LocalDate.now().with(DayOfWeek.MONDAY)
                .plusWeeks(-1), LocalTime.MIN);
        return Date.from(beginDayOfLastWeek.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取上周的结束时间
     *
     * @return
     */
    public static Date getEndDayOfLastWeek() {
        LocalDateTime endDayOfLastWeek = LocalDateTime.of(LocalDate.now().with(DayOfWeek.MONDAY)
                .plusWeeks(-1), LocalTime.MAX);
        return Date.from(endDayOfLastWeek.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取本月的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfMonth() {
        LocalDateTime beginDayOfMonth = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth())
                , LocalTime.MIN);
        return Date.from(beginDayOfMonth.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取本月的结束时间
     *
     * @return
     */
    public static Date getEndDayOfMonth() {
        LocalDateTime endDayOfMonth = LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth())
                , LocalTime.MAX);
        return Date.from(endDayOfMonth.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取上月的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfLastMonth() {
        LocalDateTime beginDayOfLastMonth = LocalDateTime.of(LocalDate.now().plusMonths(-1)
                        .with(TemporalAdjusters.firstDayOfMonth())
                , LocalTime.MIN);
        return Date.from(beginDayOfLastMonth.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取上月的结束时间
     *
     * @return
     */
    public static Date getEndDayOfLastMonth() {
        LocalDateTime beginDayOfLastMonth = LocalDateTime.of(LocalDate.now().plusMonths(-1)
                        .with(TemporalAdjusters.lastDayOfMonth())
                , LocalTime.MAX);
        return Date.from(beginDayOfLastMonth.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取本年的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfYear() {
        LocalDateTime beginDayOfYear = LocalDateTime.of(LocalDate.now()
                        .with(TemporalAdjusters.firstDayOfYear())
                , LocalTime.MIN);
        return Date.from(beginDayOfYear.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取去年的今天
     *
     * @return
     */
    public static Date getDayOfPreviousYear() {
        LocalDateTime dayOfPreviousYear = LocalDateTime.of(LocalDate.now().plusYears(-1)
                , LocalTime.MIN);
        return Date.from(dayOfPreviousYear.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取本年前几年的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfFrontYear(int before) {
        LocalDateTime beginDayOfFrontYear = LocalDateTime.of(LocalDate.now().plusYears(-before)
                        .with(TemporalAdjusters.firstDayOfYear())
                , LocalTime.MIN);
        return Date.from(beginDayOfFrontYear.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取本年的结束时间
     *
     * @return
     */
    public static Date getEndDayOfYear() {
        LocalDateTime endDayOfYear = LocalDateTime.of(LocalDate.now()
                        .with(TemporalAdjusters.lastDayOfYear())
                , LocalTime.MAX);
        return Date.from(endDayOfYear.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 获取本年前几年的结束时间
     *
     * @return
     */
    public static Date getEndDayOfFrontYear(int before) {
        LocalDateTime endDayOfFrontYear = LocalDateTime.of(LocalDate.now().plusYears(-before)
                        .with(TemporalAdjusters.lastDayOfYear())
                , LocalTime.MAX);
        return Date.from(endDayOfFrontYear.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 获取今年是哪一年
     *
     * @return
     */
    public static Integer getNowYear() {
        return LocalDate.now().getYear();
    }

    /**
     * 获取本月是哪一月
     *
     * @return
     */
    public static int getNowMonth() {
        return LocalDate.now().getMonthValue();
    }

    /**
     * 两个日期相减得到的天数
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static long getDiffDays(Date beginDate, Date endDate) {
        LocalDate begin = beginDate.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
        LocalDate end = endDate.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
        return begin.until(end, ChronoUnit.DAYS);
    }

    /**
     * 获取两个日期中的最大日期
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static Date max(Date beginDate, Date endDate) {
        return beginDate.before(endDate) ? endDate : beginDate;
    }

    /**
     * 获取两个日期中的最小日期
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static Date min(Date beginDate, Date endDate) {
        return beginDate.before(endDate) ? beginDate : endDate;
    }

    /**
     * 返回某月该季度的第一个月
     *
     * @param date
     * @return
     */
    public static Date getFirstSeasonDate(Date date) {
        LocalDateTime start = date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
        int quarter = (start.getMonthValue() - 1) / 3 + 1;

        start = start.withMonth(quarter * 2 + 1);
        System.out.println(quarter);
        System.out.println(start);

        return Date.from(start.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 返回某个日期下几天的日期
     */
    public static Date getNextDay(Date date, int i) {

        LocalDateTime nextDay = date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime()
                .plusDays(i);
        return Date.from(nextDay.toInstant(ZoneOffset.of("+8")));
    }

    /**
     * 返回某个日期前几天的日期
     *
     * @param date
     * @param i
     * @return
     */
    public static Date getFrontDay(Date date, int i) {
        LocalDateTime frontDay = date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime()
                .plusDays(-i);
        return Date.from(frontDay.toInstant(ZoneOffset.of("+8")));
    }


    /**
     * 功能描述：返回日
     *
     * @param date Date 日期
     * @return 返回日份
     */
    public static int getDay(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate().getDayOfMonth();
    }

    /**
     * 功能描述：返回小时
     *
     * @param date 日期
     * @return 返回小时
     */
    public static int getHour(Date date) {

        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime().getHour();
    }

    /**
     * 功能描述：返回分
     *
     * @param date 日期
     * @return 返回分钟
     */
    public static int getMinute(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime().getMinute();
    }

    /**
     * 返回秒钟
     *
     * @param date Date 日期
     * @return 返回秒钟
     */
    public static int getSecond(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime().getSecond();
    }


    /**
     * 功能描述：返回周几
     *
     * @param date 日期
     * @return 返回周几
     */
    public static Integer getDayofweek(Date date) {

        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate().getDayOfWeek().getValue();
    }

    /**
     * 增加指定小时
     *
     * @param date
     * @param hour
     * @return
     */
    public static Date addHour(Date date, int hour) {
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), zoneId);

        dateTime = dateTime.plusHours(hour);
        ZonedDateTime zonedDateTime = dateTime.atZone(zoneId);
        return Date.from(zonedDateTime.toInstant());
    }

    /**
     * 增加指定天数
     *
     * @param date
     * @param day
     * @return
     */
    public static Date addDay(Date date, int day) {
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), zoneId);

        dateTime = dateTime.plusDays(day);
        ZonedDateTime zonedDateTime = dateTime.atZone(zoneId);
        return Date.from(zonedDateTime.toInstant());
    }

    /**
     * 获取month所在月的所有天
     *
     * @param month
     * @return
     */
    public static List<Date> getAllDaysOfMonth(Date month) {
        List<Date> rs = new ArrayList<>();
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDateTime dateTime = LocalDateTime.ofInstant(month.toInstant(), zoneId);
        // 该月天数
        int lengthOfMonth = dateTime.toLocalDate().lengthOfMonth();
        for (int i = 1; i < lengthOfMonth + 1; i++) {
            LocalDateTime localDateTime = dateTime.withDayOfMonth(i);
            ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
            rs.add(Date.from(zonedDateTime.toInstant()));
        }
        return rs;
    }

    /**
     * 获取month所在月天数
     *
     * @param month
     * @return
     */
    public static int getDaysOfMonth(Date month) {
        List<Date> rs = new ArrayList<>();
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDateTime dateTime = LocalDateTime.ofInstant(month.toInstant(), zoneId);
        // 该月天数
        return dateTime.toLocalDate().lengthOfMonth();
    }

    /**
     * 获取指定日期区间所有天
     *
     * @param begin
     * @param end
     * @return
     */
    public static List<Date> getSpecifyDaysOfRange(Date begin, Date end) {
        List<Date> rs = new ArrayList<>();
        LocalDate startDate = LocalDate.parse(DatesUtil.FORMATDATE.format(begin), DateTimeFormatter.ofPattern(DatesUtil.FORMATDATE.getPattern()));
        LocalDate endDate = LocalDate.parse(DatesUtil.FORMATDATE.format(end), DateTimeFormatter.ofPattern(DatesUtil.FORMATDATE.getPattern()));
        ZoneId zoneId = ZoneId.systemDefault();

        if (endDate.isAfter(startDate)) {
            for (LocalDate curDate = startDate; !curDate.isAfter(endDate); curDate = curDate.plusDays(1)) {
                LocalDateTime localDateTime = curDate.atStartOfDay();
                ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
                rs.add(Date.from(zonedDateTime.toInstant()));
            }
        }

        return rs;
    }

}
